/*
 * ClientStartingGameStateController.cpp
 *
 *  Created on: Dec 27, 2011
 *      Author: rudi
 */
#include <wx/socket.h>
#include "ClientStartingGameStateController.h"
#include "RemoteTurnSubStateController.h"
#include "IdleGameStateController.h"
#include "GameStartedGameStateController.h"
#include "LevelDownloadingGameStateController.h"
#include "SmartPointer.h"
#include "MainGameController.h"
#include "MainFrame.h"
#include "MainPanel.h"
#include "NetworkEventData.h"
#include "Configuration.h"
#include "App.h"

/**
 * The signaling between the client and the server is the following
 *
 * Client                                                      Server
 *   | Connect (TCP level)                                       |
 *   |---------------------------------------------------------->|
 *   |                   CONNECTING(LevelName)/CONNECTING_REJECT |
 *   |<----------------------------------------------------------|
 *   | DOWNLOADING_START(Filename)                               |
 *   |---------------------------------------------------------->|
 *   |  DOWNLOADING_START_ACK(FileSize)/DOWNLOADING_START_REJECT |
 *   |<----------------------------------------------------------|
 *   |                                    DOWNLOADING_DATA(Data) |
 *   |<----------------------------------------------------------|
 *   | DOWNLOADING_DATA_ACK                                      |
 *   |---------------------------------------------------------->|
 *   |                                    DOWNLOADING_DATA(Data) |
 *   |<----------------------------------------------------------|
 *   | DOWNLOADING_DATA_ACK                                      |
 *   |---------------------------------------------------------->|
 *   |         (Downloading data in 1 kilobyte packages)         |
 *   |                                           DOWNLOADING_END |
 *   |<----------------------------------------------------------|
 *   | CONNECTING_ACK                                            |
 *   |---------------------------------------------------------->|
 *   |                                  CONFIGURATION(Settings*) |
 *   |<----------------------------------------------------------|
 *   | CONFIGURATION_ACK(ChosenFrequency)                        |
 *   |---------------------------------------------------------->|
 *   |                                      NEW_LEVEL(LevelName) |
 *   |<----------------------------------------------------------|
 *   | NEW_LEVEL_REJECT/DOWNLOAD_START(FileName)                 |
 *   |---------------------------------------------------------->|
 *   |  DOWNLOADING_START_ACK(FileSize)/DOWNLOADING_START_REJECT |
 *   |<----------------------------------------------------------|
 *   |                                    DOWNLOADING_DATA(Data) |
 *   |<----------------------------------------------------------|
 *   | DOWNLOADING_DATA_ACK                                      |
 *   |---------------------------------------------------------->|
 *   |                                    DOWNLOADING_DATA(Data) |
 *   |<----------------------------------------------------------|
 *   | DOWNLOADING_DATA_ACK                                      |
 *   |---------------------------------------------------------->|
 *   |         (Downloading data in 1 kilobyte packages)         |
 *   |                                           DOWNLOADING_END |
 *   |<----------------------------------------------------------|
 *   | NEW_LEVEL_ACK                                             |
 *   |---------------------------------------------------------->|
 *   |                                  CONFIGURATION(Settings*) |
 *   |<----------------------------------------------------------|
 *   | CONFIGURATION_ACK(ChosenFrequency)                        |
 *   |---------------------------------------------------------->|
 *
 *   Settings: Frequency, dot size, level settings
 */

ClientStartingGameStateController::ClientStartingGameStateController(const wxString& host, int portNumber): mLogoDrawController(), mIsNewLevel(false)
{
	// Now we are just trying to connect to a server
	wxIPV4address address;
	wxSocketClient* socket = NULL;
	IGameController* gameController = wxGetApp().getMainFrame()->getMainPanel()->getGameController();

	// If the client socket is not NULL, there is no need to do the connection - in this case we are changing to a new level
	if(NULL != wxGetApp().getClientSocket())
	{
		mIsNewLevel = true;

		return;
	}
	wxGetApp().getMainFrame()->GetStatusBar()->SetStatusText(wxT("Connecting to server: ") + host);
	if((false == address.Service(portNumber)) || (false == address.Hostname(host)))
	{
		wxGetApp().getMainFrame()->showMessageDialog(wxT("Failed to initialize client socket address!"), MainFrame::CLIENT_ERROR);
		// Revert back to Idle state
		gameController->changeState(new IdleGameStateController());

		return;
	}
	socket = new wxSocketClient();
	if(false == socket->Connect(address))
	{
		socket->Destroy();
		socket = NULL;
		wxGetApp().getMainFrame()->showMessageDialog(wxT("Failed to start client socket!"), MainFrame::CLIENT_ERROR);
		// Revert back to Idle state
		gameController->changeState(new IdleGameStateController());

		return;
	}
	if(false == socket->IsOk())
	{
		socket->Destroy();
		socket = NULL;
		wxGetApp().getMainFrame()->showMessageDialog(wxT("Failed to connect to the server"), MainFrame::CLIENT_ERROR);
		// Revert back to Idle state
		gameController->changeState(new IdleGameStateController());

		return;
	}
	// Set the socket to send events to the MainGameController
	socket->SetEventHandler(*(static_cast<MainGameController*>(gameController)));
	socket->SetNotify(wxSOCKET_INPUT_FLAG | wxSOCKET_OUTPUT_FLAG | wxSOCKET_CONNECTION_FLAG | wxSOCKET_LOST_FLAG);
	socket->Notify(true);
	wxGetApp().setClientSocket(socket);
	wxGetApp().getMainFrame()->GetStatusBar()->SetStatusText(wxT("Client started trying to connect to the server"));
	// Disabling menu points
	wxGetApp().getMainFrame()->setHostEnabled(false);
	wxGetApp().getMainFrame()->setJoinEnabled(false);
	wxGetApp().getMainFrame()->setConfigureEnabled(false);
}

/*virtual*/
ClientStartingGameStateController::~ClientStartingGameStateController()
{
	// Nothing to do yet
}

//
// Methods inherited from IController
//

void ClientStartingGameStateController::handleMouseDown(const wxPoint&)
{
	// Nothing to do
}

void ClientStartingGameStateController::handleMouseUp(const wxPoint&)
{
	// Nothing to do
}

bool ClientStartingGameStateController::handleMouseMotion(const wxPoint&)
{
	// Nothing to do
	return false;
}

const wxBitmap& ClientStartingGameStateController::getBitmapToPaint() const
{
	return mLogoDrawController.getBitmapToPaint();
}

wxBitmap& ClientStartingGameStateController::getBitmapToDraw()
{
	return mLogoDrawController.getBitmapToDraw();
}

//
// Methods inherited from IGameStateController
//

void ClientStartingGameStateController::handleSocketConnection(wxSocketEvent&)
{
	// TODO: Why is this method invoked on Windows?
	//wxASSERT_MSG(false, wxT("ClientStartingGameStateController::handleSocketConnection() invoked. It could not happen!"));
}

void ClientStartingGameStateController::handleSocketLost(wxSocketEvent&)
{
	wxASSERT_MSG(false, wxT("ClientStartingGameStateController::handleSocketLost() invoked. It could not happen!"));
}

void ClientStartingGameStateController::handleSocketInput(wxSocketEvent& socketEvent)
{
	SmartPointer<NetworkEventData> data = NULL;
	SmartPointer<Configuration::CommonConfigurationPacket> configurationData = NULL;
	RemoteTurnSubStateController* remoteTurnController = NULL;
	wxSocketBase* socket = wxGetApp().getClientSocket();
	IGameController* gameController = wxGetApp().getMainFrame()->getMainPanel()->getGameController();
	int frequency = wxGetApp().getConfiguration()->getNetworkConfiguration()->getFrequency();
	int chosenFrequency = 0;
	wxString message;

	// If this event is not happened on our socket, there is nothing to do.
	if(socketEvent.GetSocket() != socket)
		return;
	data = new NetworkEventData();
	if(false == data->read(socket))
	{
		// Something went wrong during data receiving
		socket->Destroy();
		socket = NULL;
		wxGetApp().setClientSocket(NULL);
		wxGetApp().getMainFrame()->showMessageDialog(wxT("Failed to receive data from the server"), MainFrame::CLIENT_ERROR);
		// Revert back to Idle state
		gameController->changeState(new IdleGameStateController());

		return;
	}
	switch(data->getDataType())
	{
		case NetworkEventData::CONNECTING:
			wxGetApp().setActualLevel(data->getStringData());
			// If the level is known on the client side, we can start the game itself
			if(NULL != wxGetApp().getConfiguration()->getLevels()->getLevel(wxGetApp().getActualLevel()))
			{
				// Sending acknowledge for the connection
				data = new NetworkEventData();
				data->setDataType(NetworkEventData::CONNECTING_ACK);
				if(false == data->write(socket))
				{
					socket->Destroy();
					socket = NULL;
					wxGetApp().setClientSocket(NULL);
					wxGetApp().getMainFrame()->showMessageDialog(wxT("Could not start connection acknowledge to the server!"), MainFrame::CLIENT_ERROR);
					// Revert back to Idle state
					gameController->changeState(new IdleGameStateController());

					return;
				}
			}
			else
			{
				if(true == wxGetApp().getConfiguration()->getNetworkConfiguration()->downloadUnknownLevels())
				{
					// We need to start downloading the level
					gameController->changeState(new LevelDownloadingGameStateController(this));
				}
				else
				{
					// TODO: Maybe we should send back a message, nost just drop the connection
					// The user turned off the automatic download of the unknown levels. We need to show an error message and revert back to Idle state
					socket->Destroy();
					socket = NULL;
					wxGetApp().setClientSocket(NULL);
					wxGetApp().getMainFrame()->showMessageDialog(wxT("The level on the server is unknown, and download is not allowed. You can set it in the Configuration menu."), MainFrame::CLIENT_ERROR);
					// Revert back to Idle state
					gameController->changeState(new IdleGameStateController());
				}
			}
			break;
		case NetworkEventData::CONFIGURATION:
			configurationData = new Configuration::CommonConfigurationPacket(data->getRawData());
			// Cache the received data
			wxGetApp().getConfiguration()->cacheDotSize(configurationData->getDotSize());
			wxGetApp().getConfiguration()->getLevels()->getLevel(wxGetApp().getActualLevel())->getVehicleA()->cachePosition(configurationData->getVehiclePositionA());
			wxGetApp().getConfiguration()->getLevels()->getLevel(wxGetApp().getActualLevel())->getVehicleB()->cachePosition(configurationData->getVehiclePositionB());
			// Negotiate the frequency
			if(0 == frequency || 0 == configurationData->getFrequency())
				chosenFrequency = 0;
			else if(frequency > configurationData->getFrequency())
				chosenFrequency = frequency;
			else
				chosenFrequency = configurationData->getFrequency();
			wxGetApp().getConfiguration()->getNetworkConfiguration()->cacheFrequency(chosenFrequency);
			// Send back the configuration acknowledge
			data = new NetworkEventData();
			data->setDataType(NetworkEventData::CONFIGURATION_ACK);
			chosenFrequency = htonl(chosenFrequency);
			data->setRawData((char*)(&chosenFrequency), sizeof(int));
			if(false == data->write(socket))
			{
				// Something went wrong during sending the data
				socket->Destroy();
				socket = NULL;
				wxGetApp().setClientSocket(NULL);
				wxGetApp().getMainFrame()->showMessageDialog(wxT("Could not send configuration acknowledge to the server!"), MainFrame::CLIENT_ERROR);
				// Revert back to Idle state
				gameController->changeState(new IdleGameStateController());

				return;
			}
			remoteTurnController = new RemoteTurnSubStateController();
			// The connection is over, we can start the game
			gameController->changeState(new GameStartedGameStateController(SmartPointer<GameStartedSubStateController>(remoteTurnController)));
			// Inform the user about the successful connection
			if(true == mIsNewLevel)
				message = wxT("The new level has been accepted. The game is started");
			else
				message = wxT("The server accepted the connection. The game is started!");
			wxGetApp().getMainFrame()->showMessageDialog(message, MainFrame::GAME_START);

			return;
			break;
		case NetworkEventData::CONNECTING_REJECT:
			// Drop the connection
			socket->Destroy();
			socket = NULL;
			wxGetApp().setClientSocket(NULL);
			wxGetApp().getMainFrame()->showMessageDialog(wxT("Connection rejected by the server!"), MainFrame::CLIENT_ERROR);
			// Revert back to Idle state
			gameController->changeState(new IdleGameStateController());

			return;
			break;
		default:
			// No assert because if the server party press the OK button earlier - when the game is started - it will start sending data immediately.
			// Just silently ignore the data
			return;
			break;
	}
}

void ClientStartingGameStateController::handleSocketOutput(wxSocketEvent&)
{
	// This method will be invoked only once, when a successful connection happens. Nothing to do yet
}

